﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms.VisualStyles;
using System.Windows.Input;
using System.Windows.Interop;
using GurLoader.GlobalHotkey;
using Moq;
using NUnit.Framework;

namespace GurLoaderWPF.Test.GlobalHotKey
{
    [TestFixture]
    public class HotKeyHostTests
    {
        [Test]
        public void CreateInstanceTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x=>x.AddHook(It.IsAny<HwndSourceHook>() ) ).Verifiable();
            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);

            Assert.IsNotNull(host);
            mockHwndSource.VerifyAll();
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreateInstanceNullHwndTest()
        {
            IHwndSource source = null;
            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(source, mockWindowsHotkeyProvider.Object);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void CreateInstanceNullWindowsProviderTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            IWindowsHotkeyProvider windowsHotkeyProvider = null;

            var host = new HotKeyHost(mockHwndSource.Object, windowsHotkeyProvider);
        }

        #region testable hotkeyhost class

        private const int WM_HotKey = 786;

        private class TestableHotKeyHost : HotKeyHost
        {
            public TestableHotKeyHost(IHwndSource hwndSource, IWindowsHotkeyProvider windowsHotkeyProvider) : base(hwndSource, windowsHotkeyProvider)
            {
            }

            public IntPtr CallRaiseWndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
            {
                return RaiseWndProc(hwnd, msg, wParam, lParam, ref handled);
            }

            public SerialCounter CurrentSerialCounter
            {
                get { return idGen; }
            }
        }
        #endregion

        [Test]
        public void RaiseWndProcNonHotKeyWindowsMessageTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            mockWindowsHotkeyProvider.Setup(x => x.RegisterWindowsHotKey(It.IsAny<IntPtr>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())).Verifiable();

            var host = new TestableHotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            ManualResetEvent mreHotKeyPressed = new ManualResetEvent(false);
            ManualResetEvent mreAction = new ManualResetEvent(false);

            var hk = new HotKey(Key.C, ModifierKeys.None, () => mreAction.Set());
            host.AddHotKey(hk);

            host.HotKeyPressed += (sender, e) =>
            {
                Assert.AreEqual(hk, e.HotKey);
                mreHotKeyPressed.Set();
            };

            bool handled = false;

            IntPtr result = host.CallRaiseWndProc(new IntPtr(0), WM_HotKey + 5, (IntPtr)host.CurrentSerialCounter.Current, new IntPtr(0), ref handled);

            Assert.IsNotNull(result);

            Assert.IsFalse(mreAction.WaitOne(0)); // should not be set so waitone returns false
            Assert.IsFalse(mreHotKeyPressed.WaitOne(0)); // same as above

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();

        }

        [Test]
        public void RaiseWndProcNoHotkeyFoundTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            mockWindowsHotkeyProvider.Setup(x => x.RegisterWindowsHotKey(It.IsAny<IntPtr>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())).Verifiable();

            var host = new TestableHotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            ManualResetEvent mreHotKeyPressed = new ManualResetEvent(false);
            ManualResetEvent mreAction = new ManualResetEvent(false);

            var hk = new HotKey(Key.C, ModifierKeys.None, () => mreAction.Set());
            host.AddHotKey(hk);

            host.HotKeyPressed += (sender, e) =>
            {
                Assert.AreEqual(hk, e.HotKey);
                mreHotKeyPressed.Set();
            };

            bool handled = false;

            IntPtr result = host.CallRaiseWndProc(new IntPtr(0), WM_HotKey, (IntPtr)host.CurrentSerialCounter.Current + 20, new IntPtr(0), ref handled);

            Assert.IsNotNull(result);

            Assert.IsFalse( mreAction.WaitOne(0) ); // should not be set so waitone returns false
            Assert.IsFalse( mreHotKeyPressed.WaitOne(0) ); // same as above

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void RaiseWndProcRaiseOnHotKeypressedEventTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            mockWindowsHotkeyProvider.Setup(x => x.RegisterWindowsHotKey(It.IsAny<IntPtr>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())).Verifiable();

            var host = new TestableHotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            ManualResetEvent mreHotKeyPressed = new ManualResetEvent(false);
            ManualResetEvent mreAction = new ManualResetEvent(false);

            var hk = new HotKey(Key.C, ModifierKeys.None, () => mreAction.Set());
            host.AddHotKey(hk);

            host.HotKeyPressed += (sender, e) =>
            {
                Assert.AreEqual(hk, e.HotKey);
                mreHotKeyPressed.Set();
            };

            bool handled = false;

            IntPtr result = host.CallRaiseWndProc(new IntPtr(0), WM_HotKey, (IntPtr)host.CurrentSerialCounter.Current, new IntPtr(0), ref handled);

            bool isMreSet = mreAction.WaitOne(1000 * 3);

            if (!isMreSet)
                Assert.Fail("Timed out waiting for hotkey callback action.");

            Assert.IsNotNull(result);
            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void RaiseWndProcHotKeyEventRaisedTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            mockWindowsHotkeyProvider.Setup(x=>x.RegisterWindowsHotKey(It.IsAny<IntPtr>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>() ) ).Verifiable();

            var host = new TestableHotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            ManualResetEvent mreHotKeyPressed = new ManualResetEvent(false);
            ManualResetEvent mreAction = new ManualResetEvent(false);

            var hk = new HotKey(Key.C, ModifierKeys.None, () => mreAction.Set());
            host.AddHotKey(hk);

            host.HotKeyPressed += (sender, e) =>
            {
                Assert.AreEqual(hk, e.HotKey);
                mreHotKeyPressed.Set();
            };

            bool handled = false;
            
            IntPtr result = host.CallRaiseWndProc(new IntPtr(0), WM_HotKey, (IntPtr) host.CurrentSerialCounter.Current, new IntPtr(0), ref handled);

            bool isMreSet = mreHotKeyPressed.WaitOne(1000*3);

            if ( ! isMreSet )
                Assert.Fail("Timed out waiting for event.");

            Assert.IsNotNull(result);
            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void HotKeyEnabledPropertyChangedTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            mockWindowsHotkeyProvider.Setup(x => x.RegisterWindowsHotKey(It.IsAny<IntPtr>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, false);
            host.AddHotKey(hk);

            hk.Enabled = true;

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void HotKeyDisabledPropertyChangedTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            mockWindowsHotkeyProvider.Setup(x => x.RegisterWindowsHotKey(It.IsAny<IntPtr>(), It.IsAny<int>(), It.IsAny<int>(), It.IsAny<int>())).Verifiable();
            mockWindowsHotkeyProvider.Setup(x=>x.UnregisterWindowsHotKey(new IntPtr(10), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);
            host.AddHotKey(hk);

            hk.Enabled = false;

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void HotKeyKeyPropertyChangedTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();
            
            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(), 
                    It.IsAny<int>(), 
                    It.IsAny<int>(), 
                    It.IsAny<int>()))
                .Verifiable();
            
            mockWindowsHotkeyProvider.Setup(x => x.UnregisterWindowsHotKey(new IntPtr(10), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);
            host.AddHotKey(hk);

            hk.Key = Key.F;

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10), 
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Exactly(2));

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>()), Times.Once());

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void HotKeyKeyPropertyChangedButDisabledTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            
            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, false);
            host.AddHotKey(hk);

            hk.Key = Key.F;

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Never);

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>()), Times.Never);

            mockHwndSource.Verify( x => x.Handle, Times.Never );
            
            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void HotKeyModifiersPropertyChangedTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()))
                .Verifiable();

            mockWindowsHotkeyProvider.Setup(x => x.UnregisterWindowsHotKey(new IntPtr(10), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);
            host.AddHotKey(hk);

            hk.Modifiers = ModifierKeys.Control | ModifierKeys.Shift;

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Exactly(2));

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>()), Times.Once());

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        public void HotKeyModifiersPropertyChangedButDisabledTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, false);
            host.AddHotKey(hk);

            hk.Modifiers = ModifierKeys.Shift | ModifierKeys.Control;

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Never);

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>()), Times.Never);

            mockHwndSource.Verify(x => x.Handle, Times.Never);

            mockHwndSource.VerifyAll();
            mockWindowsHotkeyProvider.VerifyAll();
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AddHotKeyNullHotKeyTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10), 
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()))
                .Verifiable();

            mockWindowsHotkeyProvider.Setup(
                x => x.UnregisterWindowsHotKey(
                    new IntPtr(10), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            host.AddHotKey(null);
        }

        [Test]
        [ExpectedException(typeof(ArgumentNullException))]
        public void AddHotKeyWithKeyValueZeroTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()))
                .Verifiable();

            mockWindowsHotkeyProvider.Setup(
                x => x.UnregisterWindowsHotKey(
                    new IntPtr(10), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(0, ModifierKeys.Alt, true);

            host.AddHotKey(hk);
        }

        [Test]
        [ExpectedException(typeof(HotKeyAlreadyRegisteredException))]
        public void AddHotKeyDuplicateHotKeyTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()))
                .Verifiable();

            mockWindowsHotkeyProvider.Setup(
                x => x.UnregisterWindowsHotKey(
                    new IntPtr(10), It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);

            host.AddHotKey(hk);
            host.AddHotKey(hk);
        }

        [Test]
        public void AddHotKeyValidHotKeyTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()))
                .Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);

            host.AddHotKey(hk);

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Once);

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>()), Times.Never);

            mockHwndSource.Verify(x => x.Handle, Times.AtLeastOnce);

            mockWindowsHotkeyProvider.VerifyAll();
            mockHwndSource.VerifyAll();
        }

        [Test]
        public void RemoveHotKeyNonRegisteredHotKeyTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);

            host.RemoveHotKey(hk);

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Never);

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>()), Times.Never);

            mockHwndSource.Verify(x => x.Handle, Times.Never);

            mockWindowsHotkeyProvider.VerifyAll();
            mockHwndSource.VerifyAll();
        }

        [Test]
        public void RemoveHotKeyValidHotKeyTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            mockWindowsHotkeyProvider.Setup(
                x => x.RegisterWindowsHotKey(
                    new IntPtr(10),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()))
                .Verifiable();

            mockWindowsHotkeyProvider.Setup(
                x => x.UnregisterWindowsHotKey(
                    new IntPtr(10), 
                    It.IsAny<int>())).Verifiable();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            HotKey hk = new HotKey(Key.C, ModifierKeys.Alt, true);

            host.AddHotKey(hk);
            host.RemoveHotKey(hk);

            mockWindowsHotkeyProvider.Verify(
                x => x.RegisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>(),
                    It.IsAny<int>(),
                    It.IsAny<int>()), Times.Once);

            mockWindowsHotkeyProvider.Verify(
                x => x.UnregisterWindowsHotKey(
                    It.IsAny<IntPtr>(),
                    It.IsAny<int>()), Times.Once);

            mockHwndSource.Verify(x => x.Handle, Times.AtLeastOnce);

            mockWindowsHotkeyProvider.VerifyAll();
            mockHwndSource.VerifyAll();
        }

        [Test]
        public void DisposeTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.RemoveHook(It.IsAny<HwndSourceHook>())).Verifiable();

            //mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            host.Dispose();

            mockHwndSource.Verify(
                x => x.AddHook(
                    It.IsAny<HwndSourceHook>()), Times.Once);

            mockHwndSource.Verify(
                x => x.RemoveHook(
                    It.IsAny<HwndSourceHook>()), Times.Once);
        }

        [Test]
        public void MultipleDisposeTest()
        {
            Mock<IHwndSource> mockHwndSource = new Mock<IHwndSource>();
            mockHwndSource.Setup(x => x.AddHook(It.IsAny<HwndSourceHook>())).Verifiable();
            mockHwndSource.Setup(x => x.RemoveHook(It.IsAny<HwndSourceHook>())).Verifiable();

            //mockHwndSource.Setup(x => x.Handle).Returns(new IntPtr(10)).Verifiable();

            Mock<IWindowsHotkeyProvider> mockWindowsHotkeyProvider = new Mock<IWindowsHotkeyProvider>();

            var host = new HotKeyHost(mockHwndSource.Object, mockWindowsHotkeyProvider.Object);
            Assert.IsNotNull(host);

            host.Dispose();
            host.Dispose();

            mockHwndSource.Verify(
                x => x.AddHook(
                    It.IsAny<HwndSourceHook>()), Times.Once);

            mockHwndSource.Verify(
                x => x.RemoveHook(
                    It.IsAny<HwndSourceHook>()), Times.Once);
        }
    }
}
